/**********************************************
*  Classes:   BoundingBox
*  Desc:      Defines the bounding box of a 3d 
*             model
*  Author:    Vaios Kalpias-Ilias (C) 2008-2009
***********************************************/

#include "BoundingBox.h"
#include "Frustrum3.h"

//-------------------------------------------------
/**
* Ctor
*/
BoundingBox::BoundingBox() : maxX(0.0f),
				  minX(0.0f),
				  maxY(0.0f),
				  minY(0.0f),
				  maxZ(0.0f),
				  minZ(0.0f)
{
	
}
//-------------------------------------------------
/**
* Ctor 2
*/
BoundingBox::BoundingBox(float _maxX,
		        float _minX,
				float _maxY,
		        float _minY,
				float _maxZ,
		        float _minZ				
				)
				: maxX(_maxX),
				  minX(_minX),
				  maxY(_maxY),
				  minY(_minY),
				  maxZ(_maxZ),
				  minZ(_minZ),
				  NUL(_minX, _maxY, _maxZ),
				  NUR(_maxX, _maxY, _maxZ),
				  NDL(_minX, _minY, _maxZ),
				  NDR(_maxX, _minY, _maxZ),
				  FUL(_minX, _maxY, _minZ),
				  FUR(_maxX, _maxY, _minZ),
				  FDL(_minX, _minY, _minZ),
				  FDR(_maxX, _minY, _minZ)
{

}
//-------------------------------------------------
/**
* Copy Ctor
*/
BoundingBox::BoundingBox(const BoundingBox& b)
		: maxX(b.maxX),
		  minX(b.minX),
		  maxY(b.maxY),
		  minY(b.minY),
		  maxZ(b.maxZ),
		  minZ(b.minZ),
		  NUL(b.NUL),
		  NUR(b.NUR),
		  NDL(b.NDL),
		  NDR(b.NDR),
		  FUL(b.FUL),
		  FUR(b.FUR),
		  FDL(b.FDL),
		  FDR(b.FDR)

{
}
//-------------------------------------------------
/**
* Dtor
*/
BoundingBox::~BoundingBox()
{
}
//-------------------------------------------------
/**
* translates and rotates the box
*/
void BoundingBox::Move(const mth::Vector3& pos, 
		const mth::Quaternion rot)
{
	mth::Vector3 temp;
	temp = pos + rot.Rotate(NUL);
	minX = temp.x;
	minY = temp.y;
	minZ = temp.z;
	maxX = temp.x;
	maxY = temp.y;
	maxZ = temp.z;
	temp = pos + rot.Rotate(NUR); 
	UpdateBounds(temp);
	temp = pos + rot.Rotate(NDL); 
	UpdateBounds(temp);
	temp = pos + rot.Rotate(NDR); 
	UpdateBounds(temp);
	temp = pos + rot.Rotate(FUL); 
	UpdateBounds(temp);
	temp = pos + rot.Rotate(FUR); 
	UpdateBounds(temp);
	temp = pos + rot.Rotate(FDL); 
	UpdateBounds(temp);
	temp = pos + rot.Rotate(FDR); 
	UpdateBounds(temp);

	UpdateVectors();
	
}
//-------------------------------------------------
/**
* grows the box if it is bigger than b
*/
void BoundingBox::Grow(const BoundingBox& b)
{
	maxX = Max(maxX, b.maxX);
	maxY = Max(maxY, b.maxY);
	maxZ = Max(maxZ, b.maxZ);
	minX = Min(minX, b.minX);
	minY = Min(minY, b.minY);
	minZ = Min(minZ, b.minZ);

	UpdateVectors();
}
//-------------------------------------------------
/**
* Scales the box
*/
void BoundingBox::Scale(const mth::Vector3& s)
{
	
	maxX *= s.x;
	minX *= s.x;
	maxY *= s.y;
	minY *= s.y;
	maxZ *= s.z;
	minZ *= s.z;

	UpdateVectors();

}
//-------------------------------------------------
/**
* Tests if the box is inside a view frustrum
*/
bool BoundingBox::IsInside(const Frustrum3& f) const
{
	if ((f.m_bottom.GetSide(NDL) == mth::Plane3::BEHIND) &&	
	   (f.m_bottom.GetSide(NDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_bottom.GetSide(NUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_bottom.GetSide(NUL) == mth::Plane3::BEHIND)	 &&
       (f.m_bottom.GetSide(FDL) == mth::Plane3::BEHIND)	 &&
	   (f.m_bottom.GetSide(FDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_bottom.GetSide(FUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_bottom.GetSide(FUL) == mth::Plane3::BEHIND))
	{
		return false;
	}

	if((f.m_top.GetSide(NDL) == mth::Plane3::BEHIND)     &&	
	   (f.m_top.GetSide(NDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_top.GetSide(NUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_top.GetSide(NUL) == mth::Plane3::BEHIND)	 &&
       (f.m_top.GetSide(FDL) == mth::Plane3::BEHIND)	 &&
	   (f.m_top.GetSide(FDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_top.GetSide(FUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_top.GetSide(FUL) == mth::Plane3::BEHIND))
	{
		return false;
	}

	if((f.m_left.GetSide(NDL) == mth::Plane3::BEHIND)    &&	
	   (f.m_left.GetSide(NDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_left.GetSide(NUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_left.GetSide(NUL) == mth::Plane3::BEHIND)	 &&
       (f.m_left.GetSide(FDL) == mth::Plane3::BEHIND)	 &&
	   (f.m_left.GetSide(FDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_left.GetSide(FUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_left.GetSide(FUL) == mth::Plane3::BEHIND))
	{
		return false;
	}

	if ((f.m_right.GetSide(NDL) == mth::Plane3::BEHIND)   &&	
	   (f.m_right.GetSide(NDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_right.GetSide(NUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_right.GetSide(NUL) == mth::Plane3::BEHIND)	 &&
       (f.m_right.GetSide(FDL) == mth::Plane3::BEHIND)	 &&
	   (f.m_right.GetSide(FDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_right.GetSide(FUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_right.GetSide(FUL) == mth::Plane3::BEHIND))
	{
		return false;
	}

	if ((f.m_near.GetSide(NDL) == mth::Plane3::BEHIND)   &&	
	   (f.m_near.GetSide(NDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_near.GetSide(NUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_near.GetSide(NUL) == mth::Plane3::BEHIND)	 &&
       (f.m_near.GetSide(FDL) == mth::Plane3::BEHIND)	 &&
	   (f.m_near.GetSide(FDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_near.GetSide(FUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_near.GetSide(FUL) == mth::Plane3::BEHIND))
	{
		return false;
	}

	if ((f.m_far.GetSide(NDL) == mth::Plane3::BEHIND)   &&	
	   (f.m_far.GetSide(NDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_far.GetSide(NUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_far.GetSide(NUL) == mth::Plane3::BEHIND)	 &&
       (f.m_far.GetSide(FDL) == mth::Plane3::BEHIND)	 &&
	   (f.m_far.GetSide(FDR) == mth::Plane3::BEHIND)	 &&
	   (f.m_far.GetSide(FUR) == mth::Plane3::BEHIND)	 &&
	   (f.m_far.GetSide(FUL) == mth::Plane3::BEHIND))
	{
		return false;
	}

	return true;
}


//-------------------------------------------------
/**
* Updates vectors
*/
void BoundingBox::UpdateVectors()
{
	NUL = mth::Vector3(minX, maxY, maxZ);
	NUR = mth::Vector3(maxX, maxY, maxZ);
	NDL = mth::Vector3(minX, minY, maxZ);
	NDR = mth::Vector3(maxX, minY, maxZ);
	FUL = mth::Vector3(minX, maxY, minZ);
	FUR = mth::Vector3(maxX, maxY, minZ);
	FDL = mth::Vector3(minX, minY, minZ);
	FDR = mth::Vector3(maxX, minY, minZ);
}

//-------------------------------------------------
/**
* Updates AABB
*/
void BoundingBox::Update(const mth::Vector3& pos, 
		        const mth::Matrix33& rot,
				float scale)
{
	/*
	scale *= 0.5f;
	mth::Vector3 xAxis = rot.GetColumn(0)*Lx*scale;// * Lx * scale;
	mth::Vector3 yAxis = rot.GetColumn(1)*Ly*scale;// * Ly * scale;
	mth::Vector3 zAxis = rot.GetColumn(2)*Lz*scale;// * Lz * scale;

	mth::Vector3 N,F,T,B,L,R;
	N = pos + zAxis;
	F = pos - zAxis;
	T = pos + yAxis;
	B = pos - yAxis;
	L = pos - xAxis;
	R = pos + xAxis;

	maxX = N.x;
	minX = N.x;
	maxY = N.y;
	minY = N.y;
	maxZ = N.z;
	minZ = N.z;
	
	
	if (maxX < F.x) maxX = F.x;
	if (maxX < T.x) maxX = T.x;
	if (maxX < B.x) maxX = B.x;
	if (maxX < L.x) maxX = L.x;
	if (maxX < R.x) maxX = R.x;


	if (maxY < F.y) maxY = F.y;
	if (maxY < T.y) maxY = T.y;
	if (maxY < B.y) maxY = B.y;
	if (maxY < L.y) maxY = L.y;
	if (maxY < R.y) maxY = R.y;

	
	if (maxZ < F.z) maxZ = F.z;
	if (maxZ < T.z) maxZ = T.z;
	if (maxZ < B.z) maxZ = B.z;
	if (maxZ < L.z) maxZ = L.z;
	if (maxZ < R.z) maxZ = R.z;

	
	if (minX > F.x) minX = F.x;
	if (minX > T.x) minX = T.x;
	if (minX > B.x) minX = B.x;
	if (minX > L.x) minX = L.x;
	if (minX > R.x) minX = R.x;

	
	if (minY > F.y) minY = F.y;
	if (minY > T.y) minY = T.y;
	if (minY > B.y) minY = B.y;
	if (minY > L.y) minY = L.y;
	if (minY > R.y) minY = R.y;


	if (minZ > F.z) minZ = F.z;
	if (minZ > T.z) minZ = T.z;
	if (minZ > B.z) minZ = B.z;
	if (minZ > L.z) minZ = L.z;
	if (minZ > R.z) minZ = R.z;
	
*/
}